<template>
  <ACheckboxGroup
    class="r-drag-checkbox-group mini-scroll-bar"
    v-bind="$attrs"
    :value="models.filter((v) => v[props.fieldNames.checked]).map((v) => v[props.fieldNames.value])"
  >
    <div
      class="r-drag-checkbox"
      ref="dragRef"
      @dragstart="onDragstart"
      @dragenter.prevent="onDragenter"
      @dragend="onDragend"
      @dragover.prevent
      @mousedown.stop="onSwitchEnable"
      @mouseup.stop
    >
      <div
        draggable="false"
        class="checkbox-item"
        v-for="item in models"
        :key="item[props.fieldNames.value]"
        @click.stop
      >
        <HolderOutlined class="checkbox-item__icon" drag-switch />
        <ACheckbox
          class="checkbox-item__checked"
          :value="item[props.fieldNames.value]"
          :disabled="handleOnceDisabled(models, item)"
          @change="onCheckedChange"
        >
          {{ item[props.fieldNames.label] }}
        </ACheckbox>
      </div>
    </div>
  </ACheckboxGroup>
</template>

<script setup>
import { ref, toValue } from 'vue';
import { HolderOutlined } from '@ant-design/icons-vue';
import { useState } from '@/hooks';

defineOptions({
  inheritAttrs: false
});

const props = defineProps({
  fieldNames: {
    type: Object,
    default: () => ({ label: 'label', value: 'value', checked: 'checked' })
  }
});

const models = defineModel('value', { default: () => [] });
const emit = defineEmits(['update:value']);

const dragRef = ref();
const [nodeTarget, setNodeTarget] = useState(null);
const [index, setIndex] = useState(-1);
const [lastIndex, setLastIndex] = useState(-1);
const [movingTarget, setMovingTarget] = useState(null);

const handleOnceDisabled = (values, item) => {
  return values.filter((v) => v[props.fieldNames.checked]).length === 1 && item[props.fieldNames.checked];
};

const onSwitchEnable = (e) => {
  // 开启拖拽
  if (e.target.closest('[drag-switch]')) {
    e.target.closest('[draggable]').draggable = true;
  }
};

const onDragstart = (e) => {
  setTimeout(() => {
    e.target.classList.add('dragging');
  }, 0);

  setNodeTarget(e.target);
  const list = [...toValue(dragRef).children];
  setIndex(list.indexOf(e.target));
  setLastIndex(list.indexOf(e.target));
};

const onDragMove = (e) => {
  const list = [...toValue(dragRef).children];
  const nodeIndex = list.indexOf(toValue(nodeTarget)); // 原节点索引
  const targetIndex = list.indexOf(e.target); //  当前节点索引

  toValue(movingTarget) && toValue(movingTarget).classList.remove('enter-before');
  toValue(movingTarget) && toValue(movingTarget).classList.remove('enter-after');
  e.target.classList.add(nodeIndex > targetIndex ? 'enter-after' : 'enter-before');
  setMovingTarget(e.target);
};

const onDragenter = (e) => {
  if (e.trigger === toValue(dragRef) || e.target === toValue(nodeTarget) || !e.target.getAttribute('draggable')) {
    return;
  }
  onDragMove(e);
  const list = [...toValue(dragRef).children];
  const targetIndex = list.indexOf(e.target); //  当前节点索引
  setLastIndex(targetIndex);
};

const onDragend = (e) => {
  setTimeout(() => {
    e.target.draggable = false;
    e.target.classList.remove('dragging');
    toValue(movingTarget) && toValue(movingTarget).classList.remove('enter-before');
    toValue(movingTarget) && toValue(movingTarget).classList.remove('enter-after');
  }, 0);

  const currentIndex = toValue(lastIndex);
  const originIndex = toValue(index);
  const values = [...toValue(models)];
  values.splice(originIndex, 1);
  values.splice(currentIndex, 0, toValue(models)[originIndex]);
  setIndex(-1);
  setLastIndex(-1);
  emit('update:value', values);
};

const onCheckedChange = (e) => {
  const { value, checked } = e.target;
  const values = [...toValue(models)];
  const index = values.findIndex((v) => v[props.fieldNames.value] === value);
  values[index][props.fieldNames.checked] = checked;
  emit('update:value', values);
};
</script>

<style lang="scss" scoped>
.r-drag-checkbox-group {
  display: block;
  width: auto;
  height: auto;
  background-color: var(--bg-color);
  position: relative;
  overflow: auto;
  max-height: 320px;
}

.r-drag-checkbox {
  width: auto;
  height: auto;

  .checkbox-item {
    width: auto;
    display: flex;
    padding: 8px;
    border-radius: 4px;
    gap: 8px;
    cursor: pointer;
    user-select: none;
    position: relative;

    .checkbox-item__icon {
      cursor: move;
    }

    .checkbox-item__checked {
      min-width: 120px;
      max-width: 200px;
    }

    &:not(.dragging):hover {
      background-color: var(--hover-bg-color);
    }
  }

  .checkbox-item.enter-after::before {
    top: 0;
    content: '';
    left: 0;
    width: 100%;
    height: 2px;
    z-index: 1;
    position: absolute;
    background-color: var(--primary-color);
  }

  .checkbox-item.enter-before::before {
    bottom: 0;
    content: '';
    left: 0;
    width: 100%;
    height: 2px;
    z-index: 1;
    position: absolute;
    background-color: var(--primary-color);
  }

  .dragging {
    opacity: 0.4;
    background-color: rgba($color: #000000, $alpha: 0.1);
    transition: all 0.3s;
  }
}
</style>
